﻿#include "FITKGraphObjectLoad_G_BF.h"

// VTK
#include <vtkArrowSource.h>
#include <vtkPoints.h>
#include <vtkVertex.h>
#include <vtkCellArray.h>
#include <vtkPolyData.h>
#include <vtkPointData.h>
#include <vtkDoubleArray.h>
#include <vtkGlyph3D.h>
#include <vtkDistanceToCamera.h>
#include <vtkActor.h>
#include <vtkProperty.h>
#include <vtkTransform.h>
#include <vtkSelectionNode.h>
#include <vtkIdTypeArray.h>

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphCommons.h"

// Data
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaLoadBodyForce.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaLoadGravity.h"
#include "FITK_Component/FITKAbaqusData/FITKAbaModelInfo.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaAssembly.h"
#include "FITK_Interface/FITKInterfaceModel/FITKModelSet.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAbstractElement.h"
#include "FITK_Kernel/FITKCore/FITKVec3D.h"

#include "FITK_Component/FITKAbaqusData/FITKAbaqusPart.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAbstractAssInstance.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaMeshPartVTK.h"

namespace Exchange
{
    FITKGraphObjectLoad_G_BF::FITKGraphObjectLoad_G_BF(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaLoadBodyForce* bForceData, int stepId, QVariant details)
        : FITKGraphObjectElementLoadBase(caseData, bForceData, stepId), m_loadType(details.isValid() ? details.toInt() : 1)
    {
        if (!bForceData)
        {
            return;
        }

        // The body force and gravity load graph is longer than others.
        m_defaultSize = 80.;

        // The gravity or body force value.
        getValue(m_stepId);

        if (m_loadType == 1)
        {
            // Generate the load's cell center poly data of element set.
            bool flag = generatePolyData(bForceData);
            if (!flag)
            {
                return;
            }

            // Initialize the load glyph size.
            m_glyphSize = m_defaultSize;
        }
        // Try to cast to a gravity load.
        else if (m_loadType == 2)
        {
            Interface::FITKAbaLoadGravity* gForceData = dynamic_cast<Interface::FITKAbaLoadGravity*>(bForceData);
            if (gForceData)
            {
                // Insert a (0, 0, 0) point and cell and value to poly data.
                double zeroPt[3]{ 0, 0, 0 };
                insertNextPoint(zeroPt, -1);
                m_idArray->InsertNextValue(0);

                // Initialize the load glyph size.
                m_glyphSize = m_defaultLargeSize;
            }
        }

        // Get force value and generate load actors.
        generateGraphs();

        // Clear cache.
        // clearCache();

        // Save the graph information.
        m_graphInfo.Type = OtherGraph;

        // Update the visibility.
        updateVisibility();
    }

    void FITKGraphObjectLoad_G_BF::update(bool forceUpdate)
    {
        this->Superclass::update(forceUpdate);

        // Resize the glyph.
        //@{
        // Get set detail information.
        Interface::FITKBoundaryLocation* load = dynamic_cast<Interface::FITKBoundaryLocation*>(_dataObj);
        if (!load)
        {
            return;
        }
              
        AbaqusData::AbaModelInfo requiredData = AbaqusData::FITKAbaModelInfo::GetModelInfo(load);
        m_glyphSize = (requiredData._set) == nullptr ? m_defaultLargeSize : m_defaultSize;

        if (!m_sourceList.isEmpty())
        {
            vtkArrowSource* arrowSource = vtkArrowSource::SafeDownCast(m_sourceList.first());
            if (arrowSource)
            {
                // Update source.
                arrowSource->SetShaftRadius(0);
                arrowSource->SetTipLength(0.2);

                // For global gravity load.
                //@{
                // The larger load arrow need a smaller radius and length.
                double scale = m_defaultSize / m_glyphSize * 1.1;
                arrowSource->SetTipRadius(scale * 0.1);
                arrowSource->SetTipLength(scale * 0.2);
                //@}
            }
        }

        for (vtkDistanceToCamera* distCamera : m_distToCameraList)
        {
            distCamera->SetScreenSize(m_glyphSize);
        }
        //@}
    }

    void FITKGraphObjectLoad_G_BF::getValue(int stepId)
    {
        // Get gravity or body force by the step index.
        Interface::FITKAbaLoadBodyForce* bForceData = dynamic_cast<Interface::FITKAbaLoadBodyForce*>(_dataObj);
        if (bForceData)
        {
            m_value[0] = bForceData->getComponent1(stepId);
            m_value[1] = bForceData->getComponent2(stepId);
            m_value[2] = bForceData->getComponent3(stepId);
        }

        m_valueForGlyph[0] = qFuzzyCompare(m_value[0], 0) ? 0 : m_value[0] / fabs(m_value[0]);
        m_valueForGlyph[1] = qFuzzyCompare(m_value[1], 0) ? 0 : m_value[1] / fabs(m_value[1]);
        m_valueForGlyph[2] = qFuzzyCompare(m_value[2], 0) ? 0 : m_value[2] / fabs(m_value[2]);
    }

    void FITKGraphObjectLoad_G_BF::generateGraphs()
    {    
        // Create source.
        vtkArrowSource* arrowSource = vtkArrowSource::New();
        arrowSource->SetShaftRadius(0);
        arrowSource->SetTipLength(0.2);
        arrowSource->SetTipResolution(m_sourceResolution);
        m_sourceList.push_back(arrowSource);

        // For global gravity load.
        //@{
        // The larger load arrow need a smaller radius and length.
        double scale = m_defaultSize / m_glyphSize;// *1.1;
        arrowSource->SetTipRadius(scale * 0.1);
        arrowSource->SetTipLength(scale * 0.2);
        //@}

        // Create actors. ( Cell centeral point load )
        generateGraph(arrowSource);
    }

    void FITKGraphObjectLoad_G_BF::insertNextPoint(double* pos, int modelId, bool addToLoadPoints)
    {
        // Details (1: Force, 2 : Moment)
        if (!m_valueForGlyph || !pos || !m_polyData || !m_points || !m_cells || !m_normal || !m_scalar)
        {
            return;
        }

        auto addPoint = [&](double* position)
        {
            int ptId = m_points->InsertNextPoint(position);

            vtkSmartPointer<vtkVertex> vertex = vtkSmartPointer<vtkVertex>::New();
            vertex->GetPointIds()->SetId(0, ptId);
            m_cells->InsertNextCell(ptId);

            if (addToLoadPoints)
            {
                m_modelIdArray->InsertNextValue(modelId);
            }
        };

        // Save the points for refreshing actors.
        if (addToLoadPoints)
        {
            m_loadPoints->InsertNextPoint(pos);
        }

        // Add tripple points, cells, normal and scalar for x, y, z load.
        if (!qFuzzyCompare(m_valueForGlyph[0], 0))
        {
            addPoint(pos);
            m_normal->InsertNextTuple3(m_valueForGlyph[0], 0, 0);
            m_scalar->InsertNextValue(1);         
        }

        if (!qFuzzyCompare(m_valueForGlyph[1], 0))
        {
            addPoint(pos);
            m_normal->InsertNextTuple3(0, m_valueForGlyph[1], 0);
            m_scalar->InsertNextValue(1);
        }

        if (!qFuzzyCompare(m_valueForGlyph[2], 0))
        {
            addPoint(pos);
            m_normal->InsertNextTuple3(0, 0, m_valueForGlyph[2]);
            m_scalar->InsertNextValue(1);
        }
    }

    void FITKGraphObjectLoad_G_BF::initActorProperties(vtkProp* actor, QVariant details)
    {
        if (!actor)
        {
            return;
        }

        // Set model actor properties.
        // Default show face and edge.
        FITKGraphActor* fActor = FITKGraphActor::SafeDownCast(actor);
        if (!fActor)
        {
            return;
        }

        // Set the scalar off.
        fActor->setScalarVisibility(false);
        fActor->GetProperty()->SetLineWidth(m_glyphLineWidth);
        fActor->GetProperty()->SetRepresentation(1);

        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_forceLoadColor, color3);
        fActor->GetProperty()->SetColor(color3);
    }
}   // namespace Exchange